<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<link rel="stylesheet" href="../css/simple.css" />
<title>组合使用构造函数模式和原型模式</title>
<script type="text/javascript">
  function Person(name,age,job){
    this.name = name;
    this.age = age;
    this.job = job;
  }
</script>
</head>
<body>
<h2>组合使用构造函数模式和原型模式</h2>
 <p class="ti2em">如果将引用类型的值定义在构造函数的原型对象中，则所有的实例中指向该属性的指针都是相同，原理如图：</p> 
 <img src="原型对象中的引用类型所有实例都继承.jpg" alt="" />
 <br />
 <p>使用构造函数模式和原型模式：</p>
 <p class="ti2em">构造函数模式：用于定义实例属性。</p>
 <p class="ti2em">原型模式：定义方法和共享的属性</p>
 <p class="ti2em">使用构造函数模式和原型模式 是将 引用类型的值 没有放在构造函数的原型对象中</p>
 <button onclick="testOne()">使用构造函数模式和原型模式  console中</button>
 <pre class="fz18">
 如：
    function testOne(){ 
      function Person(name,age,job){ 
        this.age = age;
        this.name = name;
        this.job = job;
        this.friends = ["aaa","bbb"];
      }
    
      Person.prototype = { 
        constructor : Person,
        sayName : function(){ 
          alert(this.name);
        }
      }

      var wfy = new Person("wfy",28,"enginner");
      wfy.friends.push("ccc");

      var lyy = new Person("lyy",27,"nurse");

      console.log(wfy.friends);   // ["aaa","bbb","ccc"]
      console.log(lyy.friends);   // ["aaa","bbb"] 
    }
 </pre>
 <script type="text/javascript">
    function testOne(){ 
      
      function Person(name,age,job){ 
        this.name = name;
        this.age = age;
        this.job = job;
        this.friends = ["aaa","bbb"];
        //alert(typeof this.sayHello != 'function');
      }
      
      Person.prototype = { 
        constructor : Person,
        sayName : function(){ 
          alert(this.name);
        }
      }
      
      var wfy = new Person("wfy",27,"enginner");
      wfy.friends.push("ccc");
      var lyy = new Person("lyy",26,"nurse");
      
      console.log(wfy.friends);
      console.log(lyy.friends);
    }
 </script>
 <p class="ti2em">根据上图看可以知晓，两者的区别</p>
 <br />
 <h2>动态原型模式</h2>
 <p class="ti2em">把所有的信息都封装在构造函数中，而通过在构造函数中初始化原型，保持了同时使用构造函数和原型的优点。</p>
<button onclick="addLL()">lilei</button>
 <pre>
    function Person(name,age,job){ 
      this.name = name;
      this.age = age;
      this.job = job;
      if( typeof this.sayName != 'function'){     // 对象属性的检索  如果构造函数中没有，继续想原型中检索
        Person.prototype.sayName = function(){
          alert(this.name);
        }
      }
    }
    Person.prototype = {
      sex : 'man',
      sayName : function(){ alert( 'sayName已经存在，不能再添加' ) }
    }
    var ll = new Person("liLei",30,'enginner');
    ll.sayName();  //liLei

    这个与直接写在原型对象中有什么区别 ？？       注意函数的执行先后顺序 // 对象属性的检索  如果构造函数中没有，继续想原型中检索
 </pre>
 <script type="text/javascript">
  function addLL(){ 
    function Person(name,age,job){ 
      this.name = name;
      this.age = age;
      this.job = job;
      if( typeof this.sayName != 'function'){ 
        Person.prototype.sayName = function(){
          alert(this.name);
        }
      }
    }
    Person.prototype = {
      sex : 'man',
      sayName : function(){ alert( 'sayName已经存在，不能再添加' ) }
    }
    var ll = new Person("liLei",30,'enginner');
    ll.sayName();  //liLei
    alert( ll.sex );
  }
 </script>
 <div>
  <p class="fz18">这个与直接写在原型对象中有什么区别 ？？       注意函数的执行先后顺序 // 对象属性的检索  如果构造函数中没有，继续想原型中检索</p>
  <p class="fz18">创建完实例过后，不能重新定义原型对象，会切断构造函数与原本原型对象之间的联系</p>
 </div>
 <br />
<h2>寄生构造函数模式</h2>
<p class="ti2em">该函数的作用仅仅是封装创建对象的代码，然后再返回新创建的对象</p>
<button onclick='patternOne()'>寄生构造函数模式</button>
<pre>
  function Person(name,age,job){ 
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){ alert(this.name) };
    return o;
  }
  var friends = new Person('wfy',25,'enginner');
  friends.sayName();

  与工厂化创建对象一样  只不过 工厂模式 不用new
</pre>
<p class="ti2em">通过调用函数在函数里边创建一个对象，将参数赋值给对象的属性，然后函数再将对象返回出来，返回的引用类型的指针。</p>
<script type="text/javascript">
  function patternOne(){
    function Person(name,age,job){ 
      var o = new Object();
      o.name = name;
      o.age = age;
      o.job = job;
      o.sayName = function(){ 
        alert(this.name);
      }
      return o;
    }
    var friends = new Person('wfy',28,'enginner');
    friends.sayName();
  }
</script>
<pre>
  下边是针对次函数中的  value.push.aplly(value,arguments) 与 value.push(arguments) 表现不一样 百度结果
  function SPecialArray(){ 
    var values = new Array();
    values.push.apply(values,arguments);  //这里的values与arguments相当于下边的arr1，arr2
    values.toPipedString = function(){
      return this.join("|");
    }
    return values;
  }
  var colors = new SpecialArray("red","blue","green");
  colors.toPipedString();   //"red|blue|green"
  values;                   //["red","blue","green"];

  不使用apply();

  function noUesApply(){ 
    var values = new Array();
    vaules.push( arguments );
    return values;
  }
  var arr = new noUesApply("111","2222","333");
  arr;   // [["111","222","333"]]
</pre>
<br />
<pre class="fz18 pl25">
    之一------(函数的劫持与对象的复制)
  关于对象的继承，一般的做法是用复制法： Object.extend 
  见protpotype.js 的实现方法:

  Object.extend = function(destination, source) ...{  
    for (property in source) ...{  
      destination[property] = source[property];  
    }  
    return destination;  
  } 
  除此以外，还有一种不太常见的方法：  Function.apply. 
  apply 方法能劫持(&lt;&lt;Ajax in Action>> 书中用到"劫持"一语，很生动啊)另外一个对象的方法， 
  继承另外一个对象的属性。 
  示范代码如下： 
  Apply示范代码

  &lt;script>  
  function Person(name,age)...{   //定义一个类，人类   
      this.name=name     //名字   
      this.age=age       //年龄  
      this.sayhello=function()...{alert("hello")}  
  }  
  function Print()...{            //显示类的属性  
      this.funcName="Print"  
      this.show=function()...{       
          var msg=[]  
          for(var key in this)...{  
              if (typeof(this[key])!="function") msg.push([key,":",this[key]].join(""))  
          }  
          alert(msg.join(" "))  
      }  
  }  
  function Student(name,age,grade,school)...{    //学生类  
      Person.apply(this,arguments)  
      Print.apply(this,arguments)  
      this.grade=grade                  //年级  
      this.school=school                    //学校  
  }  
  var p1=new Person("jake",10)  
  p1.sayhello()  
  var s1=new Student("tom",13,6,"清华小学")  
  s1.show()  
  s1.sayhello()  
  alert(s1.funcName)  
  &lt;/script> 
  学生类本来不具备任何方法，但是在 Person.apply(this,arguments)  后，他就具备了 Person类的sayhello方法和 所有属性。  在 Print.apply(this,arguments) 后就自动得到了  show() 方法。

  本文，作为抛砖引玉，只对 apply 的用法(在对象继承和函数劫持方面)做个小示范，其他更深入的应用要 
  靠大家慢慢去领会了。

  之二------利用Apply的参数数组化来提高

  我们再来聊聊Function.apply() 在提升程序性能方面的技巧。

  我们先从 Math.max() 函数说起，  Math.max后面可以接任意个参数，最后返回所有参数中的最大值。

  比如  
  alert(Math.max(5,8))   //8
  alert(Math.max(5,7,9,3,1,6))   //9

  但是在很多情况下，我们需要找出数组中最大的元素。
  var arr=[5,7,9,1]
  alert(Math.max(arr))    //   这样却是不行的。一定要这样写

  function getMax(arr){
      var arrLen=arr.length;
      for(var i=0,ret=arr[0];i&lt;arrLen;i++){
          ret=Math.max(ret,arr[i]);        
      }
      return ret;
  }
  这样写麻烦而且低效。如果用 apply呢，看代码：
  function getMax2(arr){
      return Math.max.apply(null,arr)
  }
  两段代码达到了同样的目的，但是getMax2却优雅，高效，简洁得多。

  看性能测试:
  getMax性能测试

  &lt;script>
  var myArr=new Array()

  function fillRnd(arrLen){  //填入 arrLen个1-10的随机数字到数组
      for(var i=0,arr=[];i&lt;arrLen;i++){
          arr[i]=Math.ceil(Math.random()*10)
      }
      return arr
  }

  function getMax(arr){
      var arrLen=arr.length;
      for(var i=0,ret=arr[0];i&lt;arrLen;i++){
          ret=Math.max(ret,arr[i]);        
      }
      return ret;
  }

  function getMax2(arr){
      return Math.max.apply(null,arr)
  }


  myArr=fillRnd(20*10000)  //生成20万个随机数填到数组

  var t1=new Date()
  var max1=getMax(myArr)
  var t2=new Date()
  var max2=getMax2(myArr)
  var t3=new Date()

  if (max1!==max2) alert("error")
  alert([t3-t2,t2-t1])   //在我机器上 96,464 .不同的机器,结果可能有差异

  &lt;/script>


  通过20万个数据的比较， getMax2 时间为 96ms 而 getmax时间为464。 两者相差5倍


   再比如数组的push方法。
  var arr1=[1,3,4];
  var arr2=[3,4,5];
  如果我们要把 arr2展开，然后一个一个追加到 arr1中去，最后让  arr1=[1,3,4,3,4,5]
  arr1.push(arr2)  显然是不行的。 因为这样做会得到  [1,3,4, [3,4,5] ]

  我们只能用一个循环去一个一个的push   (当然也可以用 arr1.concat(arr2) 但是concat方法并不改变 arr1本身)
  var arrLen=arr2.length
  for(var i=0;i&lt;arrLen;i++){
      arr1.push(arr2[i])
  }

  自从有了 Apply    ,事情就变得如此简单

  Array.prototype.push.apply(arr1,arr2)
</pre>
</body>
</html>
